package com.demo.reflectasm;

import cn.hutool.core.util.ReflectUtil;
import cn.lingyangwl.framework.tool.core.ReflectUtils;
import cn.lingyangwl.framework.tool.core.UUIDUtils;
import com.esotericsoftware.reflectasm.ConstructorAccess;
import com.esotericsoftware.reflectasm.FieldAccess;
import com.esotericsoftware.reflectasm.MethodAccess;
import org.apache.commons.lang3.time.StopWatch;
import org.junit.jupiter.api.Test;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.TimeUnit;

/**
 * @author shenguangyang
 */
public class ReflectAsmTest {
    /**
     * JDK反射调用方法
     */
    @Test
    public void testJdkReflect() throws Exception {
        ReflectAsmResp target = new ReflectAsmResp();
        long start = System.currentTimeMillis();
        Method method = target.getClass().getMethod("update", int.class, String.class);
        for (int i = 0; i < 100000000; i++) {
            method.invoke(target, 1, "zhangsan");
        }
        long end = System.currentTimeMillis();
        System.out.println("timeout=" + (end - start));//809 753 880 875 816 
    }

    /**
     * jdk 反射来set/get字段值
     */
    @Test
    public void testJdkFieldAccess() {
        StopWatch stopWatch = StopWatch.createStarted();
        Field targetField = null;
        String uuid = UUIDUtils.fastUUID();
        for (int i = 0; i < 200000; i++) {
            ReflectAsmResp target = new ReflectAsmResp();
            List<Field> fields = ReflectUtils.getFields(target);
            targetField = Optional.ofNullable(targetField)
                    .orElse(fields.stream().filter(f -> f.getName().equals("name")).findFirst().get());
            try {
                targetField.set(target, uuid);
                targetField.get(target);
            } catch (IllegalAccessException e) {
                throw new RuntimeException(e);
            }
        }
        System.out.println("time: " + stopWatch.getTime(TimeUnit.MILLISECONDS) + " ms");
    }

    /**
     * ReflectAsm反射调用方法
     * 用名称定位反射方法
     */
    @Test
    public void testReflectAsm4Name() {
        ReflectAsmResp target = new ReflectAsmResp();
        MethodAccess access = MethodAccess.get(ReflectAsmResp.class);//生成字节码的方式创建ReflectAsmRespMethodAccess
        long start = System.currentTimeMillis();
        for (int i = 0; i < 100000000; i++) {
            access.invoke(target, "update", 1, "zhangsan");
        }
        long end = System.currentTimeMillis();
        System.out.println("timeout=" + (end - start));//523 382 415 489 482
    }

    /**
     * ReflectAsm反射调用方法
     * 用方法和字段的索引定位反射方法，性能高
     */
    @Test
    public void testReflectAsm4Index() {
        ReflectAsmResp target = new ReflectAsmResp();
        MethodAccess access = MethodAccess.get(ReflectAsmResp.class);
        int index = access.getIndex("update", int.class, String.class);
        long start = System.currentTimeMillis();
        for (int i = 0; i < 100000000; i++) {
            access.invoke(target, index, 1, "zhangsan");
        }
        long end = System.currentTimeMillis();
        System.out.println("timeout=" + (end - start));//12 15 23 14 24
    }

    /**
     * ReflectAsm反射来set/get字段值
     */
    @Test
    public void testFieldAccess() {
        StopWatch stopWatch = StopWatch.createStarted();
        for (int i = 0; i < 10000; i++) {
            ReflectAsmResp target = new ReflectAsmResp();
            FieldAccess fieldAccess = FieldAccess.get(target.getClass());
            fieldAccess.set(target, "state", 1);
            fieldAccess.get(target, "state");
        }
        System.out.println("time: " + stopWatch.getTime(TimeUnit.MILLISECONDS) + " ms");
    }

    /**
     * ReflectAsm反射来调用构造方法
     */
    @Test
    public void testConstructorAccess() {
        ConstructorAccess<ReflectAsmResp> constructorAccess = ConstructorAccess.get(ReflectAsmResp.class);
        ReflectAsmResp ReflectAsmResp = constructorAccess.newInstance();
        System.out.println(ReflectAsmResp);
    }

    /**
     * 查找方法的索引
     */
    @Test
    public void testIndex() {
        ReflectAsmResp target = new ReflectAsmResp();
        MethodAccess methodAccess = MethodAccess.get(target.getClass());
        int index = methodAccess.getIndex("update", int.class, String.class);
        System.out.println(index);
    }
}
